package nl.weeaboo.sh.level;

import java.awt.Point;
import java.awt.Rectangle;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import java.util.Map.Entry;

import nl.weeaboo.sh.Game;
import nl.weeaboo.sh.model.AreaModel;
import nl.weeaboo.sh.model.RoomEnemyModel;
import nl.weeaboo.sh.model.RoomModel;
import nl.weeaboo.sh.model.TileModel;
import nl.weeaboo.sh.model.TileSetIndex;
import nl.weeaboo.sh.model.TileModel.Solidity;
import nl.weeaboo.sh.objects.Monster;
import timon.common.Log;

public class Room {
	
	protected Level level;
	protected AreaModel area;
	protected RoomModel model;
	
	private Rectangle cameraBounds;
	private Map<Point, Tile> tiles;

	public Room(Level level, AreaModel area, RoomModel model) {
		this.level = level;
		this.area = area;
		this.model = model;
		
		tiles = new HashMap<Point, Tile>();
	}
	
	//Functions
	public void loadResources(boolean spawnEnemies) {
		int t = 0;
		int layers[] = new int[model.getLayerIndices().size()];
		for (int l : model.getLayerIndices()) {
			layers[t++] = l;
		}
		
		for (int y = 0; y < model.getHeight(); y++) {
			for (int x = 0; x < model.getWidth(); x++) {				
				createTile(layers, new Point(x, y));
			}
		}
		
		if (spawnEnemies) {
			for (Entry<Point, RoomEnemyModel> entry : model.getEnemies().entrySet()) {
				createEnemy(entry.getKey(), entry.getValue());
			}
		}

		recalculateCameraBounds();
	}
	public void unloadResources() {
		for (Tile tile : tiles.values()) {
			tile.dispose();
		}
		tiles.clear();
	}
	
	public void recalculateCameraBounds() {
		int ts = Level.tileSize;		
		cameraBounds = new Rectangle(model.getX()*ts, model.getY()*ts,
				model.getWidth()*ts, model.getHeight()*ts);
		
		if (model.isLeftOpen()) {
			cameraBounds.x += -1000000;
			cameraBounds.width += 1000000;
		}
		if (model.isTopOpen()) {
			cameraBounds.y += -1000000;
			cameraBounds.height += 1000000;
		}
		if (model.isRightOpen()) {
			cameraBounds.width += 1000000;
		}
		if (model.isBottomOpen()) {
			cameraBounds.height += 1000000;
		}		
	}
	
	public void removeTile(Point p) {
		removeTile(tiles.get(p));
	}
	public void removeTile(Tile t) {
		for (Iterator<Entry<Point, Tile>> i = tiles.entrySet().iterator(); i.hasNext(); ) {
			Entry<Point, Tile> entry = i.next();
			if (entry.getValue() == t) {
				t.dispose();
				i.remove();
				return;
			}
		}
	}
	
	public void createTile(int layers[], Point pos) {
		TileSetIndex index0 = model.getTile(0, pos);
		TileModel tm = new TileModel();
		tm.setSolidity(Solidity.NONE);
		if (index0 != null) {
			TileModel m = level.getTileSet(index0.getTileSet().getId())
					.getTileModel(index0.getX(), index0.getY());
			if (m != null) tm = m;
		}
		
		Tile t = new Tile(level, this, tm, layers);

		for (int layer : layers) {
			TileSetIndex index = model.getTile(layer, pos);
			if (index != null) {
				TileSet ts = level.getTileSet(index.getTileSet().getId());
				int tx = index.getX();
				int ty = index.getY();
				t.setImage(layer, ts.getImage(tx, ty), ts.getEditorImage(tx, ty));				
			}
		}

		tiles.put(pos, t);		
	}
	public void createEnemy(Point pos, RoomEnemyModel model) {
		try {
			Class<?> c = Class.forName(model.getClassName());
			Constructor<?> constr = c.getConstructor(Game.class, Level.class);
			Monster m = (Monster)constr.newInstance(level.getGame(), level);

			m.setXY((getX()+pos.x)*Level.tileSize,
					(getY()+pos.y)*Level.tileSize);
			level.addSprite(m);
		} catch (Exception e) {
			Log.error(e);
		}
	}
	
	//Getters
	public RoomModel getModel() { return model; }
	
	public String getId() { return model.getId(); }
	public Rectangle getCameraBounds() { return cameraBounds; }
	public Tile getTile(Point p) { return tiles.get(p); }
	public int getX() { return model.getX(); }
	public int getY() { return model.getY(); }
	public int getWidth() { return model.getWidth(); }
	public int getHeight() { return model.getHeight(); }
	public boolean isBottomWall() { return model.isBottomWall(); }
	public boolean isLeftWall() { return model.isLeftWall(); }
	public boolean isRightWall() { return model.isRightWall(); }
	public boolean isTopWall() { return model.isTopWall(); }
	
	public SortedSet<Integer> getLayerIndices() { return model.getLayerIndices(); }
	public Map<Point, TileSetIndex> getModelTiles(int layer) { return model.getTiles(layer); }
	
	//Setters
	public void setOpenSides(boolean leftOpen, boolean rightOpen, boolean topOpen, boolean bottomOpen) {
		model.setOpenSides(leftOpen, rightOpen, topOpen, bottomOpen);
		recalculateCameraBounds();
	}
	public void setWallSides(boolean leftWall, boolean rightWall, boolean topWall, boolean bottomWall) {
		model.setWallSides(leftWall, rightWall, topWall, bottomWall);
	}
	
}
